/**
 * @FileName: _public.h
 * @Author: 李波(Edwin Lee)
 * @Date: 2024-04-19 01:03:44
 * @Version: 1.0
 * @Description: 通用的函数和类
 */
#ifndef __PUBLIC_H__
#define __PUBLIC_H__

#include <ctime>
#include <memory>
#include <string>
#include <chrono>
#include <cstring>
#include <regex>
#include <cstring>
#include <fstream>
#include <sstream>
#include <cstdarg>
#include <mutex>
#include <sys/stat.h>
#include <sys/types.h>

#ifdef WIN32
#include <io.h>
#include <direct.h> 
#include <windows.h>
#else
#include <unistd.h>
#endif

#ifdef WIN32
#define MKDIR(path) _mkdir(path)
#define SLEEP(mseconds) Sleep(mseconds)
#else
#define MKDIR(path) mkdir(path,0755)
#define SLEEP(mseconds) usleep(mseconds*1000)
#endif


/*
 ******************************************************************************
 *NoCopyable的派生类可以正常的构造和析构,但是派生类无法进行拷贝构造和赋值操作
 ******************************************************************************
*/
class NoCopyable
{
public:
	NoCopyable(const NoCopyable&) = delete;
	void operator=(const NoCopyable&) = delete;

protected:
	NoCopyable() = default;
	~NoCopyable() = default;
};

/*
 ************************************************************************
* 字符串操作函数和类
* C++11以上
* ***********************************************************************
*/


//把字符串中的小写字母转换成大写字母，字符串中不是字母的字符不改变
//str:待转换的字符串，支持char[]和string两种类型
void toUpper(char* str) noexcept;

//把字符串中的小写字母转换成大写字母，字符串中不是字母的字符不改变
//str:待转换的字符串，支持char[]和string两种类型
void toUpper(std::string& str) noexcept;

//把字符串中的大写字母转换成小写字母，字符串中不是字母的字符不改变
//str:待转换的字符串，支持char[]和string两种类型
void toLower(char* str) noexcept;

//把字符串中的大写字母转换成小写字母，字符串中不是字母的字符不改变
//str:待转换的字符串，支持char[]和string两种类型
void toLower(std::string& str) noexcept;

//删除字符串str开头的一个或多个ch字符
//如果ch='\ n',则会删除开头的所有\ n和\ r\ n
//成功返回true 失败返回false
bool deleteLChar(char* str, const char ch) noexcept;

//删除字符串str末尾的一个或多个ch字符
//如果ch='\ n',则会删除末尾的所有\ n和\ r\ n
//成功返回true 失败返回false
bool deleteRChar(char* str, const char ch) noexcept;

//从字符串中去掉头部的空格和\ t，尾部的空格，\ r,\ n，\ t字符，保存到strOut
// 需要保证strOut足够大函数内部不做判断
//strOut不需要清零
//strOut或者strIn为NULL返回false
bool strTrim(char* strOut, const char* strIn) noexcept;

//去掉str头部的空格和\ t，尾部的空格，\ r,\ n，\ t字符
void strTrim(std::string& str) noexcept;


//Description: 从字符串str中把oldstr替换成newstr，函数只会替换一次，不会循环替换
//Param: 
//  -oldstr 旧的字符串 
//  -newstr 新的字符串
//Return: 返回值是替换后string字符串的智能指针,出错返回空指针
//Author&Date: 李波(Edwin Lee) 2024-05-06 21:06:45
std::shared_ptr<std::string> strReplace(char *str,const char *oldstr,const char *newstr);

//从路径中去除头部空格，尾部的空格,\\r,\\n,\\t字符，再把路径中的'\\'换成‘/’
//需要保证strOut足够大函数内部不做判断
//strOut不需要清零

void pathTrim(char* pathOut, const char* pathIn) noexcept;

//判断一个字符是不是数字
bool isNum(char ch);
//判断字符串是纯数字不含其他符号（可以是0开头）
//函数内不做strTrim（）修剪
bool isNum(const char* num);

//判断字符串是整数(前面可以有+-号,也可以有多个0开头）
//函数内不做strTrim（）修剪
bool isInt(const char* integer);

//判断字符串是32位的整数(前面可以有+-号）范围：-2147483648~2147483647
//函数内不做strTrim（）修剪
bool isInt32(const char* integer);

//判断字符串是32位的正整数(前面可以有+号）范围：0~4294967295
//函数内不做strTrim（）修剪
bool isUInt32(const char* integer);

//判断字符串是64位的整数(前面可以有+-号）范围：-9223372036854775808~9223372036854775807
//函数内不做strTrim（）修剪
bool isInt64(const char* integer);

//判断字符串是64位的正整数(前面可以有+号）范围：0~18446744073709551615
//函数内不做strTrim（）修剪
bool isUInt64(const char* integer);

//判定字符串是email
//函数内不做strTrim（）修剪
bool isMail(const char* email);

//判定字符串是国内手机号码
//函数内不做strTrim（）修剪
bool isMobileNum(const char* number);

//判定字符串是合法的账号：[a-zA-Z_0-9]组成且首位是字母，账号长度为6-18位
//函数内不做strTrim（）修剪
bool isAccount(const char* account);

//判定字符串是合法的IPV4：xxx.xxx.xxx.xxx
//函数内不做strTrim（）修剪
bool isIPV4(const char* ip);

//把字符串表示的时间转换为整数表示的时间
//stime:字符串表示的时间，格式必须按顺序出现yyyy(>=1900)、mm(<=12)、dd(<=31)、hh24(<=23)、mi(<-59)、ss(<=59)一个都不能少，大小不能错
//例如2029-07-06 23:11:33
//返回值：整数表示的时间，如果stime格式不正确或者数值有异常，返回-1
time_t strToTime(const char* stime);

//用于拆分字符串的类，可以提取出相应的字符串值
class SplitStrs
{
public:
	std::vector<std::string> vString_;

	SplitStrs();
	SplitStrs(const std::string& src, const char* delimiter, const bool trim=true);
	~SplitStrs();

	//拆分函数，调用此函数会清空已经拆分的结果，返回值是拆分的个数
	// delimiter是拆分的分隔符（注意此处是字符串而不是单个字符）
	//如果trim==true,拆分会对拆分的结果进行strTrim(),从字符串中去掉头部的空格和\ t，尾部的空格，\ r,\ n，\ t字符
	//返回值是拆分处理的字符串的个数
	size_t split(const std::string& src, const char* delimiter, const bool trim=true);

	//返回拆分后的字符串个数
	size_t count() noexcept;

	//提取第index个数组的字符串到value
	//len为str的容量大小
	//成功返回true失败返回false
	bool getValue(const size_t index, char* value, const size_t len=0);

	//提取第index个数组的字符串到value
	//成功返回true失败返回false
	bool getValue(const size_t index, std::string& value);

	//提取第index个数组的字符串到value
	//函数内会判断此字符串是否为32位整数（数字与前面的+-号结合的数，数值前可以有任意多的0）
	//成功返回true失败返回false
	bool getValue(const size_t index, int32_t& value);

	//提取第index个数组的字符串到value
	//函数内会判断此字符串是否为32位正整数（数字与前面的+号结合的数，数值前可以有任意多的0）
	//成功返回true失败返回false
	bool getValue(const size_t index, uint32_t& value);

	//提取第index个数组的字符串到value
	//函数内会判断此字符串是否为64位整数（数字与前面的+-号结合的数，数值前可以有任意多的0）
	//成功返回true失败返回false
	bool getValue(const size_t index, int64_t& value);

	//提取第index个数组的字符串到value
	//函数内会判断此字符串是否为64位整数（数字与前面的+号结合的数，数值前可以有任意多的0）
	//成功返回true失败返回false
	bool getValue(const size_t index, uint64_t& value);

	//提取第index个数组的字符串到value
	//函数内 不会 判断此字符串是否为double类型以及数值大小是否已经超过double的大小
	//成功返回true失败返回false
	bool getValue(const size_t index, double& value);

	//提取第index个数组的字符串到value
	//此时字符串可以为true，false，0，1，四种，不区分大小写
	//成功获取返回true 并填写value，失败返回false，对value无操作
	bool getValue(const size_t index, bool& value);

private:
	bool isClear_;			//判断拆分的vector是否已经清空
};


/*--------------------获取xml字符串中数据的函数-----------------------------*/

//获取xml字符串中的tag标签的值（字符串）
//tag字符长度不超过60
//value:存放值的容器,需要自行保证容器大小足够
//size:value的大小
bool getXMLValue(const char* xml, const char* tag, char* value,size_t size);
bool getXMLValue(const std::string& xml, const std::string& tag, std::string& value);

//获取xml字符串中的tag标签的值（bool）
//字符串只能是true，false，1，0四种否则返回false
//value:存放值的容器
bool getXMLValue(const char* xml, const char* tag, bool* value);

//获取xml字符串中的tag标签的值（int32_t）范围：-2147483648~2147483647
//函数不做数值合法性判断
//value:存放值的容器
bool getXMLValue(const char* xml, const char* tag, int32_t* value);

//获取xml字符串中的tag标签的值（uint32_t）范围：0~4294967295
//函数不做数值合法性判断
//value:存放值的容器
bool getXMLValue(const char* xml, const char* tag, uint32_t* value);

//获取xml字符串中的tag标签的值（double）
//函数不做数值合法性判断
//double字符的长度不超过23
//value:存放值的容器
bool getXMLValue(const char* xml, const char* tag, double* value);
//获取xml字符串中的tag标签的值（int64_t）范围：-9223372036854775808~9223372036854775807
//函数不做数值合法性判断
//value:存放值的容器
bool getXMLValue(const char* xml, const char* tag, int64_t* value);


/*
 ********************************************************************************************
 *时间操作类LTime 和一个计时器 LTimer
 ********************************************************************************************
 */
//localtime（）返回静态分配的tm* ，也就是说所有线程操作的是同一个tm* ，因此是线程不安全的
//此处使用安全的localtime_s(WINDOWS),或者localtime_r(LINUX)
#ifdef WIN32
#define SLocaltime(time,result) localtime_s(result,time)
#else
#define SLocaltime(time,result) localtime_r(time,result)
#endif

//struct tm
//{
//    int tm_sec;  /*秒，正常范围0-59， 但允许至61*/
//    int tm_min;  /*分钟，0-59*/
//    int tm_hour; /*小时， 0-23*/
//    int tm_mday; /*日，即一个月中的第几天，1-31*/
//    int tm_mon;  /*月， 从一月算起，0-11*/  1 + p->tm_mon;
//    int tm_year;  /*年， 从1900至今已经多少年*/  1900＋ p->tm_year;
//    int tm_wday; /*星期，一周中的第几天， 从星期日算起，0-6*/
//    int tm_yday; /*从今年1月1日到目前的天数，范围0-365*/
//    int tm_isdst; /*日光节约时间的旗标*/
//};
//
class LTime:public NoCopyable
{
public:
	//默认构造函数,使用默认构造函数表示获取的是当前时间
	LTime();

	//使用此构造函数表示需要获取time时间戳所表示的时间
	LTime(const time_t* time);
	~LTime();

	//调用getCurrentTime()设置为当前时间
	//isCurrentTime_设置为true
	void setTimeNow();

	//设置为某个时间time
	void setTime(const time_t* time);

	//获取某种格式的日期字符串,成功返回时间字符串，失败返回空字符串
	//默认格式为yyyy-MM-dd hh24:mm:ss的日期字符串
	//如果isCurrentTime_ == true,此函数每次调用都会先调用getCurrentTime()函数刷新当前时间
	//支持以下格式：
	//yyyy-mm-dd hh24:mi:ss
	//yyyy-mm-dd hh24:mi
	//yyyy-mm-dd hh24
	//yyyy-mm-dd
	//yyyy-mm
	//yyyymmddhh24miss
	//yyyymmddhh24mi
	//yyyymmddhh24
	//yyyymmdd
	//hh24miss
	std::string getDate(const char* fmt = nullptr);


	//获取yyyy格式的年字符串
	//此函数在isCurrentTime == true时不会刷新当前时间，若要获取更精准时间请先调用setTimeNow();
	std::string getYear();

	//获取mm格式的月字符串
	//此函数在isCurrentTime == true时不会刷新当前时间，若要获取更精准时间请先调用setTimeNow();
	std::string getMonth();

	//获取dd格式的当月第几天的字符串
	//此函数在isCurrentTime == true时不会刷新当前时间，若要获取更精准时间请先调用setTimeNow();
	std::string getDay();

	//获取hh24格式的小时的字符串
	//此函数在isCurrentTime == true时不会刷新当前时间，若要获取更精准时间请先调用setTimeNow();
	std::string getHour();

	//获取mi格式的分钟的字符串
	//此函数在isCurrentTime == true时不会刷新当前时间，若要获取更精准时间请先调用setTimeNow();
	std::string getMinute();

	//获取ss格式的秒字符串
	//此函数在isCurrentTime == true时不会刷新当前时间，若要获取更精准时间请先调用setTimeNow();
	std::string getSeconds();

private:
	tm* tm_;

	//是否取当前时间
	bool isCurrentTime_;		

	//把时间设置为当前时间
	void getCurrentTime();


};

//一个计算时间段的计时器std::chrono版本
class LTimer:public NoCopyable
{
public:
	LTimer();
	~LTimer() = default;
	//开始计时
	void start();
	//获取时间段，并调用start()刷新时间
	double getElapsed();
private:
	//上次计时的时间点
	std::chrono::time_point<std::chrono::high_resolution_clock> startTime_;
	//本次计时的时间点
	std::chrono::time_point<std::chrono::high_resolution_clock> endTime_;
};



/*
 *************************************************************************************
 *dir目录操作的一些函数
 *************************************************************************************
 */
constexpr int MAX_DIR_LENGTH = 320;				//定义目录的路径最大值

//逐级创建嵌套目录
//此方法适用跨平台
//path：目录的路径
//isFileName:此路径是否包含了文件名，true-是，false-不是，默认不含文件名
//返回值：如果目录存在或者不存在但是可以创建成功返回true，创建失败返回false
bool createDir(const char* path, bool isFileName = false);

//打开文件，如果以写的方式打开，会创建文件路径
//FOPEN函数的参数和返回值与fopen库函数完全相同
//开发中用此函数代替fopen
FILE* FOPEN(const char* filename, const char* mode);

//获取文件大小
//出错返回（size_t)-1 ,实际是size_t的最大值
size_t fileSize(const char* filename);

//获取文件最后一次修改的时间（文件内容修改时间，权限等修改不算修改内容）
//成功返回true填写mtime时间，失败返回false，mtime为空
//filename:文件名
//mtime：接收时间字符串的容器
//fmt：时间的输出格式,支持以下格式：
//yyyy-mm-dd hh24:mi:ss
//yyyy-mm-dd hh24:mi
//yyyy-mm-dd hh24
//yyyy-mm-dd
//yyyy-mm
//yyyymmddhh24miss
//yyyymmddhh24mi
//yyyymmddhh24
//yyyymmdd
//hh24miss
bool fileMTime(const char* filename, std::string& mtime, const char* fmt = "yyyymmddhh24miss");


//删除filename文件，如果删除失败经过100000毫秒（0.1秒）后再次删除，最多删除times次
//删除次数建议不超过3次
bool REMOVE(const char* fileneme, const int times = 1);

//移动或者重命名（文件或文件夹）,
//如果新的路径上级目录不存在则会自动创建目录（注意：如果创建失败会留下新建的目录，函数不会把它们删除）
//oldpath:需要移动的文件名
//newpath:新的文件名
//idFileName:是否是文件名，默认true，如果oldpath是文件夹，此处填false
//times:如果修改失败，最多尝试的次数
bool RENAME(const char* oldpath, const char* newpath, bool isFileName = true, const int times=1);

//复制文件（不复制目录）
// pathFrom:复制源
// pathTo:复制的目标地址
//注意：
// 1）在复制文件之前，会自动创建dstfilename参数中的目录名。
// 2）复制文件的过程中，采用临时文件命名的方法，复制完成后再改名为dstfilename，避免中间状态的文件被读取。
// 3）复制后的文件的时间与原文件相同，这一点与Linux系统cp命令不同。
//bool copyFile(const char* pathFrom, const char* pathTo);


//使用原子类型实现的自旋锁
//自旋锁对于短期锁死的代码来说会比非自旋锁开销小，如果代码锁死时间较长则自旋锁的cpu开销较大不适用
//此代码跨平台使用，不过由于linux平台有自带的自旋锁我们也可以使用linux自带的
class SpinLock
{
public:
	SpinLock() {};
	~SpinLock() {};
	void lock()
	{
		while (flag.test_and_set(std::memory_order_acquire));
	}
	void unlock()
	{
		flag.clear(std::memory_order_release);
	}
private:
	std::atomic_flag flag = ATOMIC_FLAG_INIT;
};


/*
 ******************************************************************************************
 *Logger日志类
 ******************************************************************************************
 */

//输出一个日志类(单例模式)
class Logger :NoCopyable
{
public:
	~Logger();
	//获取一个唯一的实例(线程安全的懒汉模式）
	static Logger& getInstance();

	//设置为debug模式,默认为非debug模式
	inline void setDebug() { debug_ = true; }

	//filename:日志文件路径，建议采用绝对路径，如果文件中的目录不存在，就先创建目录(默认为当前目录）
	//openmode：日志的打开模式，与fopen库函数打开文件的方式相同默认"a+"
	//backup:是否备份文件（文件超过maxLogFileSize时会备份文件）,在多进程的服务程序中，如果多
	// 个进程公用一个日志文件，backup必须为false
	//cache:是否启用文件缓存机制，如果启用那么写进日志文件中的内容不会立即写入，默认false
	bool open(std::string filename = "log.log", const char* openmode = "a+"
		, bool backup = true, bool cache = false, size_t maxLogFileSize = 100);

	//关闭日志文件
	void close();

	//设置日志大小的最大值，size>=10,如果size<10会自动设置为10
	void setMaxLogFileSize(size_t size);

	//输出日志信息
	
	//info:普通日志信息
	bool info(const char* fmt,...);
	//fatal：致命信息
	bool fatal(const char* fmt,...);
	//debug：测试信息，如debug_==false(默认)输出为空，需要先调用setDebug()
	bool debug(const char* fmt,...);
	//error：普通错误信息
	bool error(const char* fmt,...);
	//输出不带标志和日期的信息（输出什么就写什么不添加其他内容）
	bool log(const char* fmt, ...);

private:
	std::string filename_;					//日志文件路径
	char openmode_[11];						//打开模式，通常为"a+"
	FILE* logfile_;							//打开的日志文件句柄
	bool cache_;							//写入日志时，是否启用操作系统的缓冲机制
	bool backup_;							//是否自动备份文件，日志文件大小超过maxLogFileSize时自动备份
	bool debug_;							//是否debug模式，影响logdebug()函数是否会输出debug信息
	size_t maxLogFileSize_;					//日志文件触发备份的阈值（单位M）,小于10的话会设置为10
	
	//自旋锁
	SpinLock spin_;							//原子类型实现的自旋锁，跨平台


	//构造函数
	Logger();
	//备份日志文件
	bool backupLogFile(LTime& time);
};



/*
 ***************************************************************************************************
 *LFile文件操作类
 ***************************************************************************************************
 */
constexpr int MAX_FILENAME_LENGTH = 320;
enum class FResult{success = 1,nodelim = 0,eof = 2,error = -1};

class LFile
{
public:
	LFile();
	LFile(const char* path ,const char* mode = "a+" ,bool cache = true);
	~LFile();

	//判断文件是否成功打开
	bool isOpened();

	//文件是否已经读到结尾
	bool isEnd();

	//关闭已打开的文件并回收资源
	void clear();

	//调用fflush（）释放缓存
	void flush();

	//打开新的文件（如果已经打开了一个文件，此文件会被关闭并打开新的文件）
	bool open(const char* path, const char* mode = "a+", bool cache = true);

	//置文件流 fp_ 的文件位置指示器为 offset 所指向的值。
	//成功返回0，否则非零
	int seek(long offset, int origin);

	//返回流fp_的文件位置指示器。
	//若流以二进制模式打开，则由此函数获得的值是从文件开始的字节数。
	//若流以文本模式打开，则由此函数返回的值未指定，且仅若作为 fseek() 的输入才有意义。
	int tell();

	// 获取已打开的文件全名，含扩展名,提取到buf中
	// buf在函数中会清零
	// bufsize:buf的长度
	// 成功返回true，失败返回false
	// 已对路径进行pathTrim()修剪
	bool getFullName(char* buf,int bufsize);
	// 静态方法，获取文件全名，含扩展名，提取到buf中
	// bufsize:buf的长度
	// 成功返回true，失败返回false
	// 没有对路径进行pathTrim()修剪,如果末尾有多余的的符号会输入到buf中
	static bool getFullName(char* buf, int bufsize, const char* path);

	// 获取已打开的文件名，不含扩展名,提取到buf中
	// buf在函数中会清零
	// bufsize:buf的长度
	// 已对路径进行pathTrim()修剪
	bool getFileName(char* buf, int bufsize);
	// 静态方法，获取文件名，不含扩展名,提取到buf中
	// buf在函数中会清零
	// bufsize:buf的长度
	// 一般不需要pathTrim()
	static bool getFileName(char* buf, int bufsize, const char* path);

	// 获取已打开的文件的扩展名,提取到buf中
	// buf在函数中会清零
	// bufsize:buf的长度
	bool getExt(char* buf, int bufsize);
	// 静态方法，获得扩展名,提取到buf中
	// buf在函数中会清零
	// bufsize:buf的长度
	// 没有对路径进行pathTrim()修剪,如果末尾有多余的的符号会输入到buf中
	static bool getExt(char* buf, int bufsize, const char* path);

	// 获取已打开的文本中当前位置第star（第一个字符star为1而不是0）个字符开始的size个字符的字符串，
	// 文本指针指到取到的最后一个字符后面,获取的字符串存到buf中
	// buf在函数中会清零
	// bufsize:buf的长度
	bool getStrs(char* buf, int bufsize, int star, int size);
	// 静态方法，获取文本中当前位置第star（第一个字符star为1而不是0）个字符开始的size个字符的字符串，
	// 文本指针指到取到的最后一个字符后面,获取的字符串存到buf中
	// buf在函数中会清零
	// bufsize:buf的长度
	static bool getStrs(char* buf, int bufsize, int star, int size, FILE* fp);

	//静态方法，函数把指针指向下一行开头
	//成功返回Return::success(1)
	//失败返回Return::(0)
	//读到文件结束返回Return::(-1)
	static FResult toNextLine(FILE* fp);

	//关闭并删除已打开的文件
	bool closeAndRemove();

	//调用fputs向文件写入内容
	int puts(const char* buf);

	//调用fwrite写入数据块
	//出错返回0
	size_t write(const void* ptr, size_t size, size_t nmemb);

	//向文件写入格式化的字符串
	//返回值为写入的字节数（不包括结尾的\0）
	//出错返回负数
	int fprintf(const char* fmt, ...);

	//调用fgetc函数输出一个char存储到int中
	//出错或者读到文本结束返回EOF（-1)
	int getc();

	//调用fread读取数据块
	//注意这里的第2和第3个参数与fread的2、3参数位置相反，每个读取字符的字节数默认为1
	//出错或者读到结尾返回0
	size_t read(void* ptr, size_t nmemb,size_t size=1);

	//获取已打开的文本当前位置到下个delimiter分隔符前的所有字节
	//fp为打开的文件
	//buf为存储的字符数组不需要初始化
	//bufsize为数组的总字节数
	//delimiter为分隔符，默认为换行符
	//igndelim为是否忽略掉分隔符，不把他写入buf，默认true（不写入）
	//返回值success-成功并且文本没有借宿，eof-文件已结束，nodelim-此次读取bufsize长度数据没有读到delimiter结束符，error-读取出错
	FResult getDelim(char* buf, int bufsize, char delimiter = '\n', bool igndelim = true);
	//获取文本当前位置到下个delimiter分隔符前的所有字节
	//fp为打开的文件
	//buf为存储的字符数组,不需要初始化
	//bufsize为数组的总字节数
	//delimiter为分隔符，默认为换行符
	//igndelim为是否忽略掉分隔符，不把他写入buf，默认true（不写入）
	//返回值success-成功并且文本没有借宿，eof-文件已结束,nodelim-此次读取bufsize长度数据没有读到delimiter结束符
	static FResult getDelim(FILE* fp , char* buf, int bufsize ,char delimiter = '\n',bool igndelim = true);

	//获取一个utf8字符(需要文本由utf8编写），函数内调用uchar.clear()清零
	//字符存进uchar
	//文件没打开返回FResult::error
	//都取到文件结尾返回FResult::eof,uchar不变
	//成功返回FResult::success
	FResult getU8Char(std::string& uchar);

	//获取一个GBK字符（需要文本由GBK编写），函数内调用uchar.clear()清零
	//字符存进uchar
	//文件没打开返回FResult::error
	//都取到文件结尾返回FResult::eof,uchar不变
	//成功返回FResult::success
	//此函数没有测试过
	FResult getGbkChar(std::string& uchar);
	

private:
	bool isOpened_;						//文件是否已经打开
	FILE* fp_;							//已打开的文件指针
	bool cache_;						//文件写入是否使用缓存true-使用，false-不使用
	char path_[MAX_FILENAME_LENGTH];	//文件路径
};

//用于加载配置文件的函数，配置文件类型为每行 key=value，‘#’开头的行为注释
//fileame 为配置文件名
//config  为加载后的结果集
//函数返回true 表明加载成功，结果写入config
//返回false   表明加载失败，config不做改变
bool loadConfigFile(std::map<std::string, std::string>& config, const std::string filename);
#endif
